import { _decorator, Component, Layout, Label, Prefab, instantiate, Widget, Color } from 'cc';
import { utils } from 'cfw';
const { tip } = utils;
const { ccclass, property, disallowMultiple, requireComponent } = _decorator;

@ccclass('cocosmos.PoetryLine')
@requireComponent(Layout)
@disallowMultiple(true)
export class PoetryLine extends Component {

    @property(Prefab) public charTemplate: Prefab = null;
    @tip("出现间隔", [0, 2, 0.01]) public interval: time.sec = 0.2;
    @tip("出现速度", [0, 10, 0.01]) public appearance: time.sec = 1;
    @tip("放大倍数") public magnify: num.ratio = 3;
    public get content(): string { return this._content; }

    protected onLoad() {
        this.clear();
    }

    public set content(arg: string) {
        if (isEmpty(arg) || !isStr(arg) || !this.charTemplate) return;
        this.clear();
        this._content = arg;
        for (let i = 0; i < this._content.length; i++) {
            const str = this._content.charAt(i);
            const char = this._claim(str);
            char.string = str;
            char.node.parent = this.node;
            this.scheduleOnce(() => char.node.setScale(this.magnify, this.magnify), i * this.interval);
        }
        this.getComponent(Layout).updateLayout();
        this.getComponent(Widget)?.updateAlignment();
    }

    public fade(duration: time.sec = 1) {
        this.unscheduleAllCallbacks();
        this._resetAll(1, 1);
        this._fading = 1;
        this._delta = -1 / duration;
    }

    public show(duration: time.sec = 1) {
        this.unscheduleAllCallbacks();
        this._resetAll(0, 1);
        this._fading = 0;
        this._delta = 1 / duration;

    }

    public clear(): void {
        this._content = '';
        this._fading = null;
        this.unscheduleAllCallbacks();
        this.node.getComponentsInChildren(Label).forEach(lbl => this._free(lbl));
    }

    private _delta: number = 0;
    private _fading: number = null;

    public update(dt: time.sec): void {
        if (isNum(this._fading)) {
            if (this._fading > 1 || this._fading < 0) {
                this._fading = null;
                return;
            }
            this._fading += this._delta * dt;
            this._resetAll(this._fading);
        } else {
            const ratio = 1 - dt * this.appearance, sub = Math.abs(this.magnify - 1) + Number.EPSILON;
            if (this.node.children.length > 1) {
                const children = this.node.children.copy();
                children.forEach(child => {
                    const lbl = child.getComponent(Label);
                    const now = lbl.node.scale.x;
                    if (now > this.magnify) return;
                    const des = lbl.node.scale.x * ratio;
                    if (des > 1) {
                        this._reset(lbl, (this.magnify - des) / sub, des);
                    }
                });
            }
        }
    }

    private _content: string = '';
    private _pool: Label[] = [];
    private _claim(name: string): Label {
        const created = this._pool.pop() ?? instantiate(this.charTemplate).getComponent(Label);
        created.node.name = name;
        return this._reset(created, 0, this.magnify + 0.01);
    }
    private _resetAll(a: num.pct, s?: num.ratio): this {
        this.node.getComponentsInChildren(Label).forEach(lbl => this._reset(lbl, a, s));
        return this;
    }
    private _reset(lbl: Label, a: num.pct, s?: num.ratio): Label {
        color.set(lbl.color);
        if (isNum(s)) lbl.node.setScale(s, s);
        color.a = 255 * a * a;
        lbl.color = color;
        return lbl;
    }
    private _free(char: Label): void {
        char.node.removeFromParent();
        this._pool.push(char);
    }
}

const color = new Color;